package at.fhooe.mcm.logic.network;

import java.nio.ByteBuffer;

public class Header
{

	public static final int		SERIALIZED_SIZE			= 16;
	private static final int	INVALID_SEQUENCE_NUMBER	= 0;
	private static final int	SIZE_OF_BITFIELD		= 32;

	private int					m_seqNumber;
	private int					m_ack;
	private int					m_ackBitfield;
	private int					m_packageId;

	public Header()
	{
		m_seqNumber = INVALID_SEQUENCE_NUMBER;
		reset();
	}

	public Header(ByteBuffer _buffer)
	{
		deserialize(_buffer);
	}

	public void serialize(ByteBuffer _buffer)
	{
		_buffer.putInt(m_packageId);
		_buffer.putInt(m_seqNumber);
		_buffer.putInt(m_ack);
		_buffer.putInt(m_ackBitfield);
	}

	public void deserialize(ByteBuffer _buffer)
	{
		m_packageId = _buffer.getInt();
		m_seqNumber = _buffer.getInt();
		m_ack = _buffer.getInt();
		m_ackBitfield = _buffer.getInt();
	}

	public int getSequenceNumber()
	{
		return m_seqNumber;
	}

	public void setSequenceNumber(int _seqNumber)
	{
		m_seqNumber = _seqNumber;
	}

	public void incSequenceNumber()
	{
		m_seqNumber++;
	}

	public void setPackageId(int _id)
	{
		m_packageId = _id;
	}

	public int getPackageId()
	{
		return m_packageId;
	}

	public void reset()
	{
		m_ack = INVALID_SEQUENCE_NUMBER;
		m_ackBitfield = 0;
	}

	public boolean isAcknowledged(int _seqNumber)
	{
		if (_seqNumber > m_ack || _seqNumber == INVALID_SEQUENCE_NUMBER)
		{
			return false;
		}

		if (_seqNumber == m_ack)
		{
			return true;
		}

		int bit = m_ack - _seqNumber - 1;
		if (bit >= SIZE_OF_BITFIELD)
		{
			return false;
		}

		return (m_ackBitfield & (1 << bit)) != 0;
	}

	public void acknowledge(int _ackNumber)
	{
		if (_ackNumber > m_ack)
		{
			int distance = _ackNumber - m_ack;
			if (distance < SIZE_OF_BITFIELD)
			{
				m_ackBitfield <<= distance;
			}
			else
			{
				m_ackBitfield = 0;
			}
			int lastAck = m_ack;
			m_ack = _ackNumber;

			// set bit for previous highest acknowledge sequence number
			if (lastAck != INVALID_SEQUENCE_NUMBER)
			{
				acknowledge(lastAck);
			}

		}
		else
		{
			int bit = m_ack - _ackNumber - 1;
			if (bit >= SIZE_OF_BITFIELD)
			{
				return;
			}
			int mask = 1 << bit;
			m_ackBitfield |= mask;
		}
	}

}
